#coding:utf-8


import sys;
import new;
import Config;
import wx;
import os;
import webbrowser;
import IniFile;
import wx.lib.rcsizer as rcs;
import wx.lib.intctrl;
import Global;
import Loading;
import string;
import ELog;
import traceback;
import array;
import time;
import numpy;  
import matplotlib;
import matplotlib.figure;
import matplotlib.backends.backend_wxagg;
import matplotlib.backends.backend_wx;

# matplotlib采用WXAgg为后台,将matplotlib嵌入wxPython中  
#matplotlib.use("WXAgg")  

try:
    from agw import fourwaysplitter as FWS
except ImportError: # if it's not there locally, try the wxPython lib.
    try:
        import wx.lib.agw.fourwaysplitter as FWS
    except ImportError:
        exit()

#菜单ID
from AppIds import *

import SerialUtil
import wx.lib.newevent

#Frame start
SERIAL_GENIUS_FS_CHAR=0x0F;
#Slash
SERIAL_GENIUS_SL_CHAR=0x1F;
          

#主要的4个窗口
class PanelMain(wx.Panel):

    def __init__(self, parent):
        
        wx.Panel.__init__(self, parent, id = ID_WINDOW_PANEL_MAIN)

        self.splitter = FWS.FourWaySplitter(self, agwStyle=wx.SP_LIVE_UPDATE);
        self.splitter.SetAGWWindowStyleFlag(wx.SP_LIVE_UPDATE)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.splitter, 1, wx.EXPAND)
        self.SetSizer(sizer)
        self.windowShowIndexs=[];

        #动态导入所有的模块并加载
        self.modules=[];
        
        for mod in Global.Modules:  
            try:
                exec 'import module.'+mod;
                exec 'self.modules.append(module.'+mod+'.'+(mod.capitalize())+'(self.splitter))';
            except:
                ELog.Trace();
            pass
            
        #初始化模块
        for mod in self.modules:
            mod.Hide();
            mod.isUsing=False;
        
        flag=False;
        for i in range(0, len(self.modules)):
            if Config.APP_CONFIG['setting']['show_modules'].find('['+str(i)+']')>=0:
                self.UseModule(i,True);
                flag=True;

        #如果没有预设的窗口，显示0 1 2 3
        if not flag:
            self.UseModule(0,True);
            self.UseModule(1,True);
            self.UseModule(2,True);
            self.UseModule(3,True);
        
    #返回已使用的模块数
    def UsedModuleNumber(self):
        return len(self.windowShowIndexs);
    
    #设置模块可用与否
    def UseModule(self, index, use):
        if index<0 or index>len(self.modules)-1:
            return;
        if use:
            if self.UsedModuleNumber()>=4:
                return;
            findWindow=False;
            for i in range(0, len(self.windowShowIndexs)):
                if self.windowShowIndexs[i]==index:
                    findWindow=True;
            if not findWindow:  
                self.windowShowIndexs.append(index);   
                Global.var['PanelTool'].checkboxLayouts[index].SetValue(True);

                self.splitter.AppendWindow(self.modules[index]);
                self.modules[index].Show();
                self.modules[index].isUsing=True;
                
        else:
            if self.UsedModuleNumber()<=1:
                return;
            findWindow=False;
            findIndex=0;
            for i in range(0, len(self.windowShowIndexs)):
                if self.windowShowIndexs[i]==index:                    
                    findIndex=i;
                    findWindow=True;
                    
            if findWindow:
                del self.windowShowIndexs[findIndex];
                Global.var['PanelTool'].checkboxLayouts[index].SetValue(False);

                self.splitter.DetachWindow(self.modules[index]);
                self.modules[index].Hide();
                self.modules[index].isUsing=False;
        
        if self.UsedModuleNumber()==1:
            self.splitter.SetExpanded(0)
        elif self.UsedModuleNumber()==2:
            self.splitter.SetExpanded(-1)
            self.splitter.SetVSplit(10000)
        else:
            self.splitter.SetExpanded(-1)
            self.splitter.SetVSplit(5000)  

# 左侧工具栏窗口   
class PanelTool(wx.Panel):

    def __init__(self, parent):

        wx.Panel.__init__(self, parent, id = ID_WINDOW_PANEL_TOOL)
        
        
        sizer = wx.BoxSizer(wx.VERTICAL)

        #串口
        ports=["None"];
        for port in SerialUtil.GetValidPort():
            ports.append('COM'+str(port+1));
        
        sizer.Add(wx.StaticText(self, -1, u"串口"), 0, wx.ALL, 2)
        self.comboPort = wx.ComboBox(self, -1, choices=ports,
                                     style=wx.CB_READONLY|wx.CB_DROPDOWN)
        
        if Config.APP_CONFIG['setting']['default_port'] in ports:
            self.comboPort.SetStringSelection(Config.APP_CONFIG['setting']['default_port']);
        else:
            self.comboPort.SetStringSelection("None")
            
        sizer.Add(self.comboPort, 0, wx.ALL , 2)

        #刷新
        self.btnRefreshPort = wx.Button(self, 0, u"刷新", size=(50,-1))
        self.btnRefreshPort.Bind(wx.EVT_BUTTON, self.OnRefreshPort)
        self.btnRefreshPort.SetSize(self.btnRefreshPort.GetBestSize())
        sizer.Add(self.btnRefreshPort, 0, wx.ALL , 2)
        
        
        #波特率
        sizer.Add(wx.StaticText(self, -1, u"波特率"), 0, wx.ALL, 2)
        baudrates=[];
        for rate in SerialUtil.BAUDRATES:
            baudrates.append(str(rate))
        self.comboBaudrate = wx.ComboBox(self, -1, choices=baudrates,
                                         style=wx.CB_READONLY|wx.CB_DROPDOWN)
        
        if Config.APP_CONFIG['setting']['default_baudrate'] in baudrates:
            self.comboBaudrate.SetStringSelection(Config.APP_CONFIG['setting']['default_baudrate']);
        else:
            self.comboBaudrate.SetStringSelection("9600")
            
        sizer.Add(self.comboBaudrate, 0, wx.ALL , 2)
        
        #数据位
        sizer.Add(wx.StaticText(self, -1, u"数据位"), 0, wx.ALL, 2)
        bytesizes=list(SerialUtil.BYTESIZES);
        default_bytesize=None;
        for i in range(0,len(bytesizes)):
            bytesizes[i]=str(bytesizes[i]);
            if Config.APP_CONFIG['setting']['default_bytesize']==bytesizes[i]:
                default_bytesize=bytesizes[i];
        bytesizes.sort();
        self.comboBytesize = wx.ComboBox(self, -1, choices=bytesizes,
                                         style=wx.CB_READONLY|wx.CB_DROPDOWN)
        
        if default_bytesize:
            self.comboBytesize.SetStringSelection(default_bytesize);
        else:
            self.comboBytesize.SetStringSelection(bytesizes[0])
            
        sizer.Add(self.comboBytesize, 0, wx.ALL , 2)
        
        #停止位
        sizer.Add(wx.StaticText(self, -1, u"停止位"), 0, wx.ALL, 2)
        stopbits=list(SerialUtil.STOPBITS);
        default_stopbit=None;
        for i in range(0,len(stopbits)):
            stopbits[i]=str(stopbits[i])
            if Config.APP_CONFIG['setting']['default_stopbits']==stopbits[i]:
                default_stopbit=stopbits[i];
        stopbits.sort();
        self.comboStopbits = wx.ComboBox(self, -1, choices=stopbits,
                                         style=wx.CB_READONLY|wx.CB_DROPDOWN)
        
        if default_stopbit:
            self.comboStopbits.SetStringSelection(default_stopbit);
        else:
            self.comboStopbits.SetStringSelection(stopbits[0])
            
        sizer.Add(self.comboStopbits, 0, wx.ALL , 2)
        
        #校验
        sizer.Add(wx.StaticText(self, -1, u"校验"), 0, wx.ALL, 2)
        paritys=[];
        default_parity=None;
        for key, value in SerialUtil.PARITY_NAMES.items():
            paritys.append(value);
            if Config.APP_CONFIG['setting']['default_parity']==value:
                default_parity=value;
        paritys.sort();
        self.comboParity = wx.ComboBox(self, -1, choices=paritys,
                                         style=wx.CB_READONLY|wx.CB_DROPDOWN)
        
        if default_bytesize:
            self.comboParity.SetStringSelection(default_parity);
        else:
            self.comboParity.SetStringSelection(paritys[0])
            
        sizer.Add(self.comboParity, 0, wx.ALL , 2)
        
        #打开
        sizer.Add(wx.StaticText(self, -1, u""), 0, wx.ALL, 2)   #占位
        self.btnOpenPort = wx.Button(self, 0, u"打开")
        self.btnOpenPort.Bind(wx.EVT_BUTTON, self.OnOpenPort)
        sizer.Add(self.btnOpenPort, 0, wx.ALL , 2)
        
        #显示所有模块
        self.checkboxLayouts=[];
        for modname in Global.ModulesName:
            cb = wx.CheckBox(self, -1, modname);
            sizer.Add(cb, 0, wx.ALL, 2);
            self.checkboxLayouts.append(cb);
            cb.Bind(wx.EVT_CHECKBOX, self.OnLayoutNumberChange);
            
        #意见反馈
        try:
            try:
                from agw import hyperlink as hl
            except ImportError:
                import wx.lib.agw.hyperlink as hl 
            sizer.Add(wx.StaticText(self, -1, u""), 0, wx.ALL, 2)   #占位  
            sizer.Add(hl.HyperLinkCtrl(self, -1, u"意见反馈", URL=Global.app['suggestion_url']))
        except:
            pass
            

        #设置布局
        border = wx.BoxSizer()
        border.Add(sizer, 1, wx.EXPAND|wx.ALL, 5)
        self.SetSizer(border)

    #更改显示模块
    def OnLayoutNumberChange(self, event):
        passed=True;
        if event.GetEventObject().GetValue():
            if Global.var['PanelMain'].UsedModuleNumber()>=4:
                wx.MessageBox(u'最多只能显示4个');
                passed=False;
        else:
            if Global.var['PanelMain'].UsedModuleNumber()<=1:
                wx.MessageBox(u'至少选择一个');
                passed=False;

        if passed:
            Config.APP_CONFIG['setting']['show_modules']='';
            for i in range(0, len(self.checkboxLayouts)):
                Global.var['PanelMain'].UseModule(i,self.checkboxLayouts[i].GetValue());
                if self.checkboxLayouts[i].GetValue():
                    Config.APP_CONFIG['setting']['show_modules']+='['+str(i)+']';

            
        else:
            event.GetEventObject().SetValue(not event.GetEventObject().GetValue());
            
        Global.var['PanelMain'].splitter.Refresh();
        Global.var['PanelMain'].Refresh();
        Global.var['PanelTool'].Refresh();
        
    #打开串口
    def OnOpenPort(self, event, forceClose=False):
        if (Global.SerialGeniusPort and Global.SerialGeniusPort.running ) or forceClose:
            Global.SerialGeniusPort.stop();
            self.btnOpenPort.SetLabel(u'打开');
            self.comboPort.Enable(True);
            self.btnRefreshPort.Enable(True);
            self.comboBaudrate.Enable(True);
            self.comboBytesize.Enable(True);
            self.comboStopbits.Enable(True);
            self.comboParity.Enable(True);
            return;
        
        if self.comboPort.GetCurrentSelection() <= 0:
            wx.MessageBox(u"请选择串口", u"提示");
            return;
        
        try:
            Config.APP_CONFIG['setting']['default_port']=self.comboPort.GetStringSelection();
            t_port=int(Config.APP_CONFIG['setting']['default_port'][3:])-1;
            
            Config.APP_CONFIG['setting']['default_baudrate']=self.comboBaudrate.GetStringSelection();
            t_baudrate=int(Config.APP_CONFIG['setting']['default_baudrate']);
            
            Config.APP_CONFIG['setting']['default_bytesize']=self.comboBytesize.GetStringSelection();
            t_bytesize=int(Config.APP_CONFIG['setting']['default_bytesize']);
            
            Config.APP_CONFIG['setting']['default_parity']=self.comboParity.GetStringSelection();
            t_parity=Config.APP_CONFIG['setting']['default_parity'];
            
            for key, value in SerialUtil.PARITY_NAMES.items():
                if t_parity==value:
                    t_parity=key;
                    break;
            
            Config.APP_CONFIG['setting']['default_stopbits']=self.comboStopbits.GetStringSelection();
            t_stopbits=float(Config.APP_CONFIG['setting']['default_stopbits']);
            if t_stopbits==1.0:
                t_stopbits=1;
            elif t_stopbits==2.0:
                t_stopbits=2;
            
        except:
            wx.MessageBox( u"打开串口失败:0x01", u"提示");
            ELog.Trace();
            return;
            

        t_timeout=2
        #print t_port, t_baudrate, t_bytesize, t_parity, t_stopbits, t_timeout
        
        Global.SerialGeniusPort=SerialUtil.SerialThread(port=t_port,
                                                 baudrate=t_baudrate,
                                                 bytesize=t_bytesize,
                                                 parity=t_parity,
                                                 stopbits=t_stopbits,
                                                 timeout=t_timeout);

        if False==Global.SerialGeniusPort.start():
            wx.MessageBox( u"打开串口失败:0x02", u"提示");
            return;
        else:
            self.comboPort.Enable(False);
            self.btnRefreshPort.Enable(False);
            self.comboBaudrate.Enable(False);
            self.comboBytesize.Enable(False);
            self.comboStopbits.Enable(False);
            self.comboParity.Enable(False);
            
            self.btnOpenPort.SetLabel(u'关闭');
    
    #刷新串口
    def OnRefreshPort(self, event):
        Loading.Show();
        self.comboPort.Clear()
        self.comboPort.Append("None");
        for port in SerialUtil.GetValidPort():
            self.comboPort.Append('COM'+str(port+1));
        self.comboPort.SetStringSelection("None");
        Loading.Hide();
        
#布局Panel
class PanelLayout(wx.Panel):
     def __init__(self, parent):

        wx.Panel.__init__(self, parent)
        sizer = wx.BoxSizer(wx.HORIZONTAL);
        
        
        Global.var['PanelTool']=PanelTool(self);
        sizer.Add(Global.var['PanelTool']);
        
        Global.var['PanelMain']=PanelMain(self);
        sizer.Add(Global.var['PanelMain'], 1, wx.EXPAND);
        
        self.SetSizer(sizer);


#主框架
class MainFrame(wx.Frame):
    
    def __init__(self, parent, title):
             
        #获取系统环境信息
        self._EnvironmentInit()
        

        wx.Frame.__init__(   self,
                             parent,
                             id=-1,
                             title=title,
                             pos=wx.DefaultPosition,
                             size=(800, 600),
                             style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.CLIP_CHILDREN )

       

        #窗口初始化
        self._WindowInit()

        #初始化状态栏
        self._StatusBarInit()
        #self._UpdateStatusBar(u"正在加载...");
        self._UpdateStatusBar(u"欢迎使用SerialGenius！");

        #设置Icon
        try:
            self.icon = wx.Icon(Global.env['app_root']+'dll/ico.dll', wx.BITMAP_TYPE_ICO);
            self.SetIcon(self.icon);
        except:
            ELog.Trace();

        
        # 设置布局
        sizer = wx.BoxSizer(wx.VERTICAL)        
        sizer.Add(PanelLayout(self), 1, wx.EXPAND)
        self.SetSizer(sizer)
        sizer.Layout()
        
        # startup window
        try:
            from agw import advancedsplash as AS
        except ImportError: # if it's not there locally, try the wxPython lib.
            import wx.lib.agw.advancedsplash as AS        
        try:
            frame = AS.AdvancedSplash(self,
                                  bitmap=wx.Bitmap(Global.env['app_root']+"dll/su.dll", wx.BITMAP_TYPE_PNG), 
                                  timeout=3000,
                                  agwStyle=AS.AS_TIMEOUT | AS.AS_CENTER_ON_SCREEN)
            # remove default click close handler
            frame.Bind(wx.EVT_LEFT_DOWN, self.OnYummy)
            frame.Bind(wx.EVT_LEFT_UP, self.OnYummy)
            frame.Bind(wx.EVT_RIGHT_DOWN, self.OnYummy)
            frame.Bind(wx.EVT_RIGHT_UP, self.OnYummy) 
        except:
            pass
        
        # version check
        import Version
        Version.AutoCheckVersion()
        
    def OnYummy(self, event):
        pass        

    ################# Private #################

    #环境初始化
    def _EnvironmentInit(self):
        
        mm=wx.DisplaySize()
        Global.env['screen']['width']=mm[0]
        Global.env['screen']['height']=mm[1]
        root = os.path.dirname(os.path.abspath(sys.argv[0]))
        if root[-1:]!=os.sep:
            root = root + os.sep
        Global.env['app_root']=root

    #窗口初始化
    def _WindowInit(self):
        #设置最小显示
        self.SetMinSize(wx.Size(400, 300))
        #关闭提示
        self.Bind(wx.EVT_CLOSE,self._OnClose)
        #在中间显示
        self.CenterOnScreen()

    #状态栏初始化
    def _StatusBarInit(self):
        self._statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
        self._statusbar.SetStatusWidths([-1, 300])
        self._statusbar.SetStatusText(Config.APP_CONFIG['copyright'],1)

    def _SafetyCloseWindow(self,event):
        # 如果串口已打开，尝试关闭
        if (Global.SerialGeniusPort and Global.SerialGeniusPort.running ):
            Global.SerialGeniusPort.stop();        

        #保存当前配置
        IniFile.Save();
        
        if event == None :
            self.Destroy()
        else:
            event.Skip()
        
        
    ################# Public #################
    #改变状态栏
    def _UpdateStatusBar(self,text):
        self._statusbar.SetStatusText(text, 0)
    

    ############## Windows Event #############
    def _OnClose(self,event):
        self._SafetyCloseWindow(event)

class MainApp(wx.App):
            
    def OnInit(self):

        #加载配置
        IniFile.Load();
        Global.var['Frame'] = MainFrame(None, Config.APP_CONFIG['name']+' V'+Config.APP_CONFIG['version']+' - '+Config.APP_CONFIG['description'])

        self.SetTopWindow(Global.var['Frame'])
        
        Global.var['Frame'].Show(True)
        return True


if __name__ == '__main__':
    app = MainApp();
    app.MainLoop();
    
    

